//
//  NSString+ZUtilities.m
//  WeClubs
//
//  Created by Zed Link on 30/3/2017.
//  Copyright © 2017 Zed. All rights reserved.
//

#import "NSString+ZUtilities.h"
#import <CommonCrypto/CommonDigest.h>

@implementation NSString (ZUtilities)

#pragma mark - 数据加密相关

- (NSString *)zSHA1String
{
    // 中文时会丢失数据
//    const char *cStr = [self cStringUsingEncoding:NSUTF8StringEncoding];
//    NSData *data = [NSData dataWithBytes:cStr length:self.length];
    
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    
    uint8_t result[CC_SHA1_DIGEST_LENGTH];
    CC_SHA1(data.bytes, (unsigned int)data.length, result);
    
    NSMutableString *hex = [NSMutableString stringWithCapacity:CC_SHA1_DIGEST_LENGTH * 2];
    for (int i = 0; i < CC_SHA1_DIGEST_LENGTH; i++) {
        [hex appendFormat:@"%02X", result[i]];
    }
    return hex;
}

- (NSString *)zMD5BiaryString
{
    const char *cStr = [self UTF8String];
    uint8_t result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(cStr, (unsigned int)strlen(cStr), result);
    
    NSMutableString *binary = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH];
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        [binary appendFormat:@"%d", (char)result[i]];
    }
    return binary;
}

- (NSString *)zMD5HexString
{
    /**
     MD5是长度为128位, 占16个字节大小, 用32个16进制数表示(一个16进制数占4位)
     一个字节能存储2个16进制数, 当结果不足2位时, 避免丢失该16进制值, 需要补0
     */
    
    const char *cString = [self UTF8String];
    uint8_t result[CC_MD5_DIGEST_LENGTH];
    CC_MD5(cString, (unsigned int)strlen(cString), result);
    
    NSMutableString *hex = [NSMutableString string];
    for (int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        // 不足两位，前面补0, 16进制输出
        [hex appendFormat:@"%02X", result[i]];
    }
    
    NSString *MD5String = [hex lowercaseString];
    return MD5String;
}

#pragma mark - 正则表达式相关

- (BOOL)zCheckPhoneNumber
{
    // ^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$
    NSString *pattern = @"^((13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0,5-9]))\\d{8}$";
    NSString *substring = [self firstSubstringWithPattern:pattern];
    
    if (substring == nil) {
        return NO;
    } else {
        ZLog(@"手机号验证正确: %@", substring);
        return YES;
    }
}

// 没有匹配结果时, 返回 NSMakeRange(NSNotFound, 0)
- (NSRange)firstRangeWithPattern:(NSString *)pattern
{
    NSError *error;
    NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
    if (error) {
        ZLog(@"正则表达式创建失败: %@", error);
        return NSMakeRange(NSNotFound, 0);
    }
    
    NSTextCheckingResult *result = [regular firstMatchInString:self options:0 range:NSMakeRange(0, self.length)];    
    return result.range;
}

// 没有匹配结果时, 返回 nil
- (NSString *)firstSubstringWithPattern:(NSString *)pattern
{
    NSRange firstRange = [self firstRangeWithPattern:pattern];
    if (firstRange.location == NSNotFound || firstRange.length == 0) {
        return nil;
    }
    
    return [self substringWithRange:firstRange];
}

// 没有匹配结果时, 返回nil; 有结果时返回包装了NSRange的NSValue数组
- (NSArray <NSValue *> *)matchRangesWithPattern:(NSString *)pattern
{
    NSError *error;
    NSRegularExpression *regular = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
    if (error) {
        ZLog(@"正则表达式创建失败: %@", error);
        return nil;
    }
    
    NSArray <NSTextCheckingResult *> *results = [regular matchesInString:self options:0 range:NSMakeRange(0, self.length)];
    if (results.count == 0 || results == nil) {
        ZLog(@"没有匹配的结果: %@", error);
        return nil;
    }
    
    NSMutableArray *ranges = [NSMutableArray arrayWithCapacity:results.count];
    for (NSTextCheckingResult *result in results) {
        NSValue *rangeValue = [NSValue valueWithRange:result.range];
        [ranges addObject:rangeValue];
    }
    return ranges;
}

// 没有匹配结果时, 返回nil; 有结果时返回NSString数组
- (NSArray <NSString *> *)matchSubstringsWithPattern:(NSString *)pattern
{
    NSArray <NSValue *> *results = [self matchRangesWithPattern:pattern];
    if (results == nil || results.count == 0) {
        return nil;
    }
    
    NSMutableArray *substrings = [NSMutableArray arrayWithCapacity:results.count];
    for (NSValue *value in results) {
        NSString *substring = [self substringWithRange:value.rangeValue];
        [substrings addObject:substring];
    }
    return substrings;
}

// 判断字符串是否有表情符号
+ (BOOL)zStringContainsEmoji:(NSString *)string {
    __block BOOL returnValue = NO;
    
    [string enumerateSubstringsInRange:NSMakeRange(0, [string length])
                               options:NSStringEnumerationByComposedCharacterSequences
                            usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
                                const unichar hs = [substring characterAtIndex:0];
                                if (0xd800 <= hs && hs <= 0xdbff) {
                                    if (substring.length > 1) {
                                        const unichar ls = [substring characterAtIndex:1];
                                        const int uc = ((hs - 0xd800) * 0x400) + (ls - 0xdc00) + 0x10000;
                                        if (0x1d000 <= uc && uc <= 0x1f77f) {
                                            returnValue = YES;
                                        }
                                    }
                                } else if (substring.length > 1) {
                                    const unichar ls = [substring characterAtIndex:1];
                                    if (ls == 0x20e3) {
                                        returnValue = YES;
                                    }
                                } else {
                                    if (0x2100 <= hs && hs <= 0x27ff) {
                                        returnValue = YES;
                                    } else if (0x2B05 <= hs && hs <= 0x2b07) {
                                        returnValue = YES;
                                    } else if (0x2934 <= hs && hs <= 0x2935) {
                                        returnValue = YES;
                                    } else if (0x3297 <= hs && hs <= 0x3299) {
                                        returnValue = YES;
                                    } else if (hs == 0xa9 || hs == 0xae || hs == 0x303d || hs == 0x3030 || hs == 0x2b55 || hs == 0x2b1c || hs == 0x2b1b || hs == 0x2b50) {
                                        returnValue = YES;
                                    }
                                }
                            }];
    
    return returnValue;
}

// 判断是否是身份证号
+ (BOOL)zJudgeIdentityStringValid:(NSString *)identityString {
    
    if (identityString.length != 18) return NO;
    // 正则表达式判断基本 身份证号是否满足格式
    NSString *regex = @"^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X)$";
    //  NSString *regex = @"^(^[1-9]\\d{7}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}$)|(^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])((\\d{4})|\\d{3}[Xx])$)$";
    NSPredicate *identityStringPredicate = [NSPredicate predicateWithFormat:@"SELF MATCHES %@",regex];
    //如果通过该验证，说明身份证格式正确，但准确性还需计算
    if(![identityStringPredicate evaluateWithObject:identityString]) return NO;
    
    //** 开始进行校验 *//
    
    //将前17位加权因子保存在数组里
    NSArray *idCardWiArray = @[@"7", @"9", @"10", @"5", @"8", @"4", @"2", @"1", @"6", @"3", @"7", @"9", @"10", @"5", @"8", @"4", @"2"];
    
    //这是除以11后，可能产生的11位余数、验证码，也保存成数组
    NSArray *idCardYArray = @[@"1", @"0", @"10", @"9", @"8", @"7", @"6", @"5", @"4", @"3", @"2"];
    
    //用来保存前17位各自乖以加权因子后的总和
    NSInteger idCardWiSum = 0;
    for(int i = 0;i < 17;i++) {
        NSInteger subStrIndex = [[identityString substringWithRange:NSMakeRange(i, 1)] integerValue];
        NSInteger idCardWiIndex = [[idCardWiArray objectAtIndex:i] integerValue];
        idCardWiSum+= subStrIndex * idCardWiIndex;
    }
    
    //计算出校验码所在数组的位置
    NSInteger idCardMod=idCardWiSum%11;
    //得到最后一位身份证号码
    NSString *idCardLast= [identityString substringWithRange:NSMakeRange(17, 1)];
    //如果等于2，则说明校验码是10，身份证号码最后一位应该是X
    if(idCardMod==2) {
        if(![idCardLast isEqualToString:@"X"]||[idCardLast isEqualToString:@"x"]) {
            return NO;
        }
    }
    else{
        //用计算出的验证码与最后一位身份证号码匹配，如果一致，说明通过，否则是无效的身份证号码
        if(![idCardLast isEqualToString: [idCardYArray objectAtIndex:idCardMod]]) {
            return NO;
        }
    }
    return YES;
}



@end
